﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;

namespace Wwtfly.DBUtility
{
    /// <summary>
    /// Emit 动态生成, 数据类型要注意, 枚举不能为可空
    /// </summary>
    public class Emit {
        /// <summary>
        /// 返回实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dr">IDataReader</param>
        /// <param name="hasNextResult">是否有多个结果集</param>
        /// <returns>T</returns>
        public static T ConvertTo<T>(IDataReader dr, bool hasNextResult = false) {
            List<T> tList = ConvertToList<T>(dr, hasNextResult);
            return tList.Count == 0 ? Activator.CreateInstance<T>() : tList[0];
        }
        /// <summary>
        /// 返回实体集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dr">IDataReader</param>
        /// <param name="hasNextResult">是否有多个结果集</param>
        /// <returns>List</returns>
        public static List<T> ConvertToList<T>(IDataReader dr, bool hasNextResult = false) {
            DataReaderDynamicEmit<T> db = DataReaderDynamicEmit<T>.CreateBuilder(dr);
            List<T> tList = new List<T>();
            while (dr.Read()) {
                tList.Add(db.Build(dr));
            }
            if (!hasNextResult) {
                dr.Close();
                dr.Dispose();
            }
            return tList;
        }
        /// <summary>
        /// 返回实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dr">DataRow</param>
        /// <returns>T</returns>
        public static T ConvertTo<T>(DataRow dr) {
            DataRowDynamicEmit<T> db = DataRowDynamicEmit<T>.CreateBuilder(dr);
            return db.Build(dr);
        }
        /// <summary>
        /// 返回实体集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dr">DataTable</param>
        /// <returns>List</returns>
        public static List<T> ConvertToList<T>(DataTable dt) {
            List<T> tList = new List<T>();
            foreach (DataRow dr in dt.Rows) {
                tList.Add(ConvertTo<T>(dr));
            }
            return tList;
        }
    }

    #region Emit

    /// <summary>
    /// DataReader 动态生成
    /// </summary>
    internal class DataReaderDynamicEmit<T> {

        private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
        private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
        private delegate T Load(IDataRecord dataRecord);
        private Load handler;

        private DataReaderDynamicEmit() { }
        /// <summary>
        /// 创建实体类
        /// </summary>
        /// <param name="dataRecord">IDataRecord</param>
        /// <returns>T(指定的实体类型)</returns>
        public T Build(IDataRecord dataRecord) {
            return handler(dataRecord);
        }
        /// <summary>
        /// 创建委托
        /// </summary>
        /// <param name="dataRecord">IDataRecord</param>
        /// <returns>DynamicBuilder</returns>
        public static DataReaderDynamicEmit<T> CreateBuilder(IDataRecord dataRecord) {
            DataReaderDynamicEmit<T> dynamicBuilder = new DataReaderDynamicEmit<T>();

            DynamicMethod method = new DynamicMethod("DataReaderDynamicCreate", typeof(T), new Type[] { typeof(IDataRecord) }, typeof(T), true);
            ILGenerator generator = method.GetILGenerator();

            LocalBuilder result = generator.DeclareLocal(typeof(T));
            generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);

            for (int i = 0; i < dataRecord.FieldCount; i++) {
                PropertyInfo propertyInfo = typeof(T).GetProperty(dataRecord.GetName(i));
                Label endIfLabel = generator.DefineLabel();

                if (propertyInfo != null && propertyInfo.GetSetMethod() != null) {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);

                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());

                    generator.MarkLabel(endIfLabel);
                }
            }

            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);

            dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
            return dynamicBuilder;
        }

    }
    /// <summary>
    /// DataRow 动态生成
    /// </summary>
    internal class DataRowDynamicEmit<T> {

        private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
        private static readonly MethodInfo isNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
        private delegate T Load(DataRow dataRecord);
        private Load handler;

        private DataRowDynamicEmit() { }
        /// <summary>
        /// 创建实体类
        /// </summary>
        /// <param name="dataRecord">DataRow</param>
        /// <returns>T(指定的实体类型)</returns>
        public T Build(DataRow dataRecord) {
            return handler(dataRecord);
        }
        /// <summary>
        /// 创建委托
        /// </summary>
        /// <param name="dataRecord">DataRow</param>
        /// <returns>DataRowDynamicEmit</returns>
        public static DataRowDynamicEmit<T> CreateBuilder(DataRow dataRecord) {
            DataRowDynamicEmit<T> dynamicBuilder = new DataRowDynamicEmit<T>();

            DynamicMethod method = new DynamicMethod("DataRowDynamicCreate", typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true);
            ILGenerator generator = method.GetILGenerator();

            LocalBuilder result = generator.DeclareLocal(typeof(T));
            generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);

            for (int i = 0; i < dataRecord.ItemArray.Length; i++) {
                PropertyInfo propertyInfo = typeof(T).GetProperty(dataRecord.Table.Columns[i].ColumnName);
                Label endIfLabel = generator.DefineLabel();

                if (propertyInfo != null && propertyInfo.GetSetMethod() != null) {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);

                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                    generator.MarkLabel(endIfLabel);
                }
            }

            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);

            dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
            return dynamicBuilder;
        }

    }

    #endregion
}
